/**
 * 坐标转换工具类
 * 支持高德地图坐标系与Three.js世界坐标系的相互转换
 */

import * as THREE from 'three'

// 投影参数（简化的墨卡托投影）
const PROJECTION_SCALE = 20037508.342789244
const EARTH_RADIUS = 6378137
const ORIGIN_SHIFT = Math.PI * EARTH_RADIUS

export interface LngLat {
  lng: number
  lat: number
}

export interface WorldPosition {
  x: number
  y: number
  z?: number
}

export interface ScreenPosition {
  x: number
  y: number
}

export class CoordinateUtils {
  private originLngLat: LngLat
  private scale: number

  constructor(originLngLat: LngLat, scale: number = 1) {
    this.originLngLat = originLngLat
    this.scale = scale
  }

  /**
   * 经纬度转墨卡托投影坐标
   */
  lngLatToMercator(lngLat: LngLat): WorldPosition {
    const x = (lngLat.lng * ORIGIN_SHIFT) / 180
    let y = Math.log(Math.tan(((90 + lngLat.lat) * Math.PI) / 360)) / (Math.PI / 180)
    y = (y * ORIGIN_SHIFT) / 180
    
    return { x, y }
  }

  /**
   * 墨卡托投影坐标转经纬度
   */
  mercatorToLngLat(position: WorldPosition): LngLat {
    const lng = (position.x / ORIGIN_SHIFT) * 180
    let lat = (position.y / ORIGIN_SHIFT) * 180
    lat = (180 / Math.PI) * (2 * Math.atan(Math.exp((lat * Math.PI) / 180)) - Math.PI / 2)
    
    return { lng, lat }
  }

  /**
   * 经纬度转Three.js世界坐标
   * 以原点为参考，计算相对位置
   */
  lngLatToWorld(lngLat: LngLat): WorldPosition {
    const originMercator = this.lngLatToMercator(this.originLngLat)
    const targetMercator = this.lngLatToMercator(lngLat)
    
    return {
      x: (targetMercator.x - originMercator.x) * this.scale,
      y: (targetMercator.y - originMercator.y) * this.scale,
      z: 0
    }
  }

  /**
   * Three.js世界坐标转经纬度
   */
  worldToLngLat(position: WorldPosition): LngLat {
    const originMercator = this.lngLatToMercator(this.originLngLat)
    
    const mercatorPosition = {
      x: originMercator.x + position.x / this.scale,
      y: originMercator.y + position.y / this.scale
    }
    
    return this.mercatorToLngLat(mercatorPosition)
  }

  /**
   * 屏幕坐标转射线
   * 用于射线检测
   */
  screenToRay(
    screenPos: ScreenPosition,
    camera: THREE.Camera,
    containerSize: { width: number; height: number }
  ): THREE.Ray {
    const mouse = new THREE.Vector2()
    mouse.x = (screenPos.x / containerSize.width) * 2 - 1
    mouse.y = -(screenPos.y / containerSize.height) * 2 + 1

    const raycaster = new THREE.Raycaster()
    raycaster.setFromCamera(mouse, camera)
    
    return raycaster.ray
  }

  /**
   * 计算两个经纬度点之间的距离（米）
   */
  calculateDistance(lngLat1: LngLat, lngLat2: LngLat): number {
    const R = EARTH_RADIUS
    const dLat = this.toRadians(lngLat2.lat - lngLat1.lat)
    const dLng = this.toRadians(lngLat2.lng - lngLat1.lng)
    
    const a = 
      Math.sin(dLat / 2) * Math.sin(dLat / 2) +
      Math.cos(this.toRadians(lngLat1.lat)) * 
      Math.cos(this.toRadians(lngLat2.lat)) *
      Math.sin(dLng / 2) * Math.sin(dLng / 2)
    
    const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a))
    
    return R * c
  }

  /**
   * 角度转弧度
   */
  private toRadians(degrees: number): number {
    return degrees * (Math.PI / 180)
  }

  /**
   * 弧度转角度
   */
  private toDegrees(radians: number): number {
    return radians * (180 / Math.PI)
  }

  /**
   * 计算方位角（从北向开始，顺时针）
   */
  calculateBearing(from: LngLat, to: LngLat): number {
    const dLng = this.toRadians(to.lng - from.lng)
    const lat1 = this.toRadians(from.lat)
    const lat2 = this.toRadians(to.lat)
    
    const x = Math.sin(dLng) * Math.cos(lat2)
    const y = Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) * Math.cos(lat2) * Math.cos(dLng)
    
    const bearing = this.toDegrees(Math.atan2(x, y))
    return (bearing + 360) % 360
  }

  /**
   * 根据距离和方位角计算目标点
   */
  calculateDestination(start: LngLat, distance: number, bearing: number): LngLat {
    const R = EARTH_RADIUS
    const bearingRad = this.toRadians(bearing)
    const lat1 = this.toRadians(start.lat)
    const lng1 = this.toRadians(start.lng)
    
    const lat2 = Math.asin(
      Math.sin(lat1) * Math.cos(distance / R) +
      Math.cos(lat1) * Math.sin(distance / R) * Math.cos(bearingRad)
    )
    
    const lng2 = lng1 + Math.atan2(
      Math.sin(bearingRad) * Math.sin(distance / R) * Math.cos(lat1),
      Math.cos(distance / R) - Math.sin(lat1) * Math.sin(lat2)
    )
    
    return {
      lat: this.toDegrees(lat2),
      lng: this.toDegrees(lng2)
    }
  }

  /**
   * 创建边界框
   */
  createBounds(center: LngLat, radiusMeters: number): {
    north: number
    south: number
    east: number
    west: number
  } {
    const north = this.calculateDestination(center, radiusMeters, 0)
    const south = this.calculateDestination(center, radiusMeters, 180)
    const east = this.calculateDestination(center, radiusMeters, 90)
    const west = this.calculateDestination(center, radiusMeters, 270)
    
    return {
      north: north.lat,
      south: south.lat,
      east: east.lng,
      west: west.lng
    }
  }

  /**
   * 检查点是否在边界框内
   */
  isPointInBounds(
    point: LngLat,
    bounds: { north: number; south: number; east: number; west: number }
  ): boolean {
    return (
      point.lat >= bounds.south &&
      point.lat <= bounds.north &&
      point.lng >= bounds.west &&
      point.lng <= bounds.east
    )
  }

  /**
   * 将Three.js向量转换为世界坐标
   */
  vector3ToWorld(vector: THREE.Vector3): WorldPosition {
    return {
      x: vector.x,
      y: vector.y,
      z: vector.z
    }
  }

  /**
   * 将世界坐标转换为Three.js向量
   */
  worldToVector3(position: WorldPosition): THREE.Vector3 {
    return new THREE.Vector3(position.x, position.y, position.z || 0)
  }

  /**
   * 高德地图像素坐标转世界坐标
   * 需要配合高德地图的API使用
   */
  amapPixelToWorld(
    pixel: ScreenPosition,
    AMap: any, // AMap构造函数
    map: any, // AMap.Map实例
    camera: THREE.Camera
  ): WorldPosition | null {
    if (!map || !AMap) return null
    
    try {
      // 将像素坐标转换为经纬度
      const lngLat = map.containerToLngLat(new AMap.Pixel(pixel.x, pixel.y))
      
      // 转换为世界坐标
      return this.lngLatToWorld({
        lng: lngLat.lng,
        lat: lngLat.lat
      })
    } catch (error) {
      console.error('坐标转换失败:', error)
      return null
    }
  }

  /**
   * 世界坐标转高德地图像素坐标
   */
  worldToAmapPixel(
    position: WorldPosition,
    AMap: any, // AMap构造函数
    map: any // AMap.Map实例
  ): ScreenPosition | null {
    if (!map || !AMap) return null
    
    try {
      // 转换为经纬度
      const lngLat = this.worldToLngLat(position)
      
      // 转换为像素坐标
      const pixel = map.lngLatToContainer(new AMap.LngLat(lngLat.lng, lngLat.lat))
      
      return {
        x: pixel.x,
        y: pixel.y
      }
    } catch (error) {
      console.error('坐标转换失败:', error)
      return null
    }
  }

  /**
   * 基于您项目的坐标转换方法
   * 世界坐标转屏幕坐标
   */
  worldToScreen(v3: THREE.Vector3, camera: THREE.Camera): THREE.Vector2 {
    const worldVector = v3.clone()
    const standardVector = worldVector.project(camera) // 世界坐标转标准设备坐标
    const a = window.innerWidth / 2
    const b = window.innerHeight / 2
    const vx = Math.round(standardVector.x * a + a) // 标准设备坐标转屏幕坐标
    const vy = Math.round(-standardVector.y * b + b) // 标准设备坐标转屏幕坐标
    
    return new THREE.Vector2(vx, vy)
  }

  /**
   * 屏幕坐标转经纬度（基于您项目的方法）
   */
  screenToLngLat(
    point: THREE.Vector2,
    AMap: any,
    map: any,
    targetV2?: THREE.Vector2
  ): THREE.Vector2 {
    const { x, y } = point
    const pixel = new AMap.Pixel(x, y)
    const lnglat = map.containerToLngLat(pixel)
    const v2 = new THREE.Vector2()
    v2.set(lnglat.lng, lnglat.lat)
    if (targetV2?.isVector2) {
      targetV2.copy(v2)
    }
    return v2
  }

  /**
   * 更新原点和缩放
   */
  updateOrigin(originLngLat: LngLat, scale?: number) {
    this.originLngLat = originLngLat
    if (scale !== undefined) {
      this.scale = scale
    }
  }

  /**
   * 获取当前配置
   */
  getConfig() {
    return {
      origin: { ...this.originLngLat },
      scale: this.scale
    }
  }
}

// 创建默认实例
export const defaultCoordinateUtils = new CoordinateUtils(
  { lng: 120.1551, lat: 30.2741 }, // 默认杭州坐标
  1
)

// 导出常用函数
export const {
  lngLatToWorld,
  worldToLngLat,
  calculateDistance,
  calculateBearing,
  createBounds,
  isPointInBounds
} = defaultCoordinateUtils